<?php

namespace library;

use support\Log;
use WeChatPay\Builder;
use WeChatPay\Crypto\Rsa;
use WeChatPay\Util\PemUtil;
use GuzzleHttp\Exception\RequestException;

class wx_pay
{
    protected $instance;

    public function __construct()
    {
        $merchantId = env('WX_PAY_MERCHANT_ID','1672563662');
        $merchantPrivateKeyFilePath = 'file:///www/wwwroot/zhnc.sanjiaomao6.fun/public/apiclient_key.pem';
        $merchantPrivateKeyInstance = Rsa::from($merchantPrivateKeyFilePath, Rsa::KEY_TYPE_PRIVATE);
        $merchantCertificateSerial = env('WX_PAY_MERCHANT_SERIAL_NO','26FB2D3C3E0496981C1A363667E3BC0AAABDCA2A');
        $apiV3Key = env('WX_PAY_API_V3_KEY');

        // 检查 APIv3 密钥是否正确
        if (empty($apiV3Key) || strlen($apiV3Key) !== 32) {
            throw new \Exception('请在 ENV 中配置正确的 32位 APIv3 密钥');
        }

        // 平台证书缓存路径
        $certPath = '/www/wwwroot/zhnc.sanjiaomao6.fun/public/platform_cert.pem';
        if (!file_exists($certPath) || time() - filemtime($certPath) > 21600) {
            $certInfo = self::downloadPlatformCert($merchantId, $merchantCertificateSerial, $merchantPrivateKeyInstance, $apiV3Key, $certPath);
        } else {
            $certInfo = [
                'serial_no' => PemUtil::parseCertificateSerialNo('file://' . $certPath),
                'cert_path' => $certPath
            ];
        }

        $platformPublicKeyInstance = Rsa::from('file://' . $certInfo['cert_path'], Rsa::KEY_TYPE_PUBLIC);

        $this->instance = Builder::factory([
            'mchid' => $merchantId,
            'serial' => $merchantCertificateSerial,
            'privateKey' => $merchantPrivateKeyInstance,
            'certs' => [
                $certInfo['serial_no'] => $platformPublicKeyInstance,
            ],
        ]);
    }



    public function jsapiTransaction($order_id, $openid, $amount)
    {
        $appid     = env("WECHAT_APPID");
        $mchid     = env("WX_PAY_MERCHANT_ID");
        $notifyUrl = env("WX_PAY_NOTIFY_URL_TEST");

        $params = [
            "appid"        => $appid,
            "mchid"        => $mchid,
            "description"  => "订单商品说明",
            "out_trade_no" => $order_id,
            "notify_url"   => $notifyUrl,
            "amount" => [
                "total"    => $amount,
                "currency" => "CNY"
            ],
            "payer" => [
                "openid" => $openid
            ]
        ];

        try {
            $resp = $this->instance
                ->chain('v3/pay/transactions/jsapi')
                ->post([
                    'json' => $params
                ]);

            $body = json_decode($resp->getBody(), true);
            $prepay_id = $body['prepay_id'] ?? '';

            if (!$prepay_id) {
                throw new \Exception("未获取到 prepay_id");
            }

            // ========== 构造前端支付参数 ==========
            $timeStamp = (string)time();
            $nonceStr = bin2hex(random_bytes(16));
            $package = "prepay_id={$prepay_id}";
            $signType = 'RSA';

            $message = "{$appid}\n{$timeStamp}\n{$nonceStr}\n{$package}\n";

            $privateKeyPath = base_path('/public/apiclient_key.pem'); // 私钥路径
            $privateKey = openssl_pkey_get_private(file_get_contents($privateKeyPath));

            openssl_sign($message, $signature, $privateKey, 'sha256WithRSAEncryption');
            $paySign = base64_encode($signature);

            return [
                'code' => 1,
                'msg'  => '下单成功',
                'data' => [
                    'timeStamp' => $timeStamp,
                    'nonceStr'  => $nonceStr,
                    'package'   => $package,
                    'signType'  => $signType,
                    'paySign'   => $paySign
                ]
            ];
        } catch (RequestException $e) {
            $error = $e->hasResponse() ? $e->getResponse()->getBody()->getContents() : $e->getMessage();
            Log::error("JSAPI下单失败：" . $error);

            return [
                'code' => 0,
                'msg' => '下单失败',
                'data' => $error
            ];
        } catch (\Throwable $e) {
            Log::error("签名失败：" . $e->getMessage());
            return [
                'code' => 0,
                'msg' => '签名失败',
                'data' => $e->getMessage()
            ];
        }
    }



    public function transfer($openid, $out_bill_no, $transfer_amount)
    {
        try {
            $resp = $this->instance
                ->chain('v3/fund-app/mch-transfer/transfer-bills')
                ->post([
                    'json' => [
                        'appid' => env('WECHAT_APPID','wxcc0dd8ebe92b9cca'),
                        'out_bill_no' => $out_bill_no,
                        'transfer_scene_id' => '1000',
                        'openid' => $openid,
                        'transfer_amount' => $transfer_amount,
                        'transfer_remark' => "订单结算奖励",
                        'user_recv_perception' => "现金奖励",
                        'transfer_scene_report_infos' => [
                            ['info_type' => "活动名称", 'info_content' => "订单结算奖励"],
                            ['info_type' => "奖励说明", 'info_content' => "其他车友购买挪车码奖励"],
                        ],
                    ]
                ]);


            Log::info("转账成功：" . $resp->getBody());
            return [
                'code' => 1,
                'msg' => '转账成功',
                'data' => $resp->getBody()
            ];

        } catch (RequestException $e) {
            Log::info("请求失败：" . $e->getResponse()->getBody());
            if ($e->hasResponse()) {
                return [
                    'code' => 0,
                    'msg' => '请求失败',
                    'data' => $e->getResponse()->getBody()
                ];
            }
            return [
                'code' => 0,
                'msg' => '请求失败',
                'data' => $e->getMessage()
            ];
        }


    }


    //撤销转账
    public function reverse($out_bill_no)
    {

        try {
            $resp = $this->instance
                ->chain("/v3/fund-app/mch-transfer/transfer-bills/out-bill-no/{$out_bill_no}/cancel")
                ->post();

            echo "撤销转账成功：" . $resp->getStatusCode() . "\n";
            echo $resp->getBody();

        } catch (RequestException $e) {
            echo "请求失败：" . $e->getMessage() . "\n";
            if ($e->hasResponse()) {
                echo $e->getResponse()->getBody();
            }
        }


    }


    //查询转账单详情
    public function query($out_bill_no)
    {

        try {
            $resp = $this->instance
                ->chain("/v3/fund-app/mch-transfer/transfer-bills/out-bill-no/{$out_bill_no}")
                ->get();

            Log::info("查询转账单详情成功：" . $resp->getBody() . "\n");

            return [
                'code' => 1,
                'msg' => '收款成功',
                'data' => json_decode($resp->getBody(),true)
            ];

        } catch (RequestException $e) {

            var_dump($e->getMessage());
            if ($e->hasResponse()) {
                Log::error("查询转账单详情-请求失败：" . $e->getResponse()->getBody() . "\n");
                return [
                    'code' => 0,
                    'msg' => '请求失败',
                    'data' => $e->getResponse()->getBody()
                ];
            }

            return [
                'code' => 0,
                'msg' => '请求失败',
                'data' => $e->getMessage()
            ];
        }


    }


    // 下载平台证书
    public static function downloadPlatformCert($merchantId, $merchantSerial, $privateKey, $apiV3Key, $certPath): array
    {
        $url = 'https://api.mch.weixin.qq.com/v3/certificates';
        $timestamp = time();
        $nonceStr = bin2hex(random_bytes(16));
        $body = '';

        $message = "GET\n/v3/certificates\n{$timestamp}\n{$nonceStr}\n{$body}\n";
        openssl_sign($message, $signature, $privateKey, 'sha256WithRSAEncryption');
        $signature = base64_encode($signature);

        $authorization = sprintf(
            'WECHATPAY2-SHA256-RSA2048 mchid="%s",nonce_str="%s",timestamp="%s",serial_no="%s",signature="%s"',
            $merchantId, $nonceStr, $timestamp, $merchantSerial, $signature
        );

        $ch = curl_init($url);
        curl_setopt_array($ch, [
            CURLOPT_HTTPHEADER => [
                'Authorization: ' . $authorization,
                'Accept: application/json',
                'User-Agent: wechatpay-demo-php/1.0.0'
            ],
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_TIMEOUT => 10,
        ]);
        $response = curl_exec($ch);
        curl_close($ch);
        $res = json_decode($response, true);

        if (!isset($res['data'][0]['encrypt_certificate'])) {
            throw new \Exception('平台证书下载失败');
        }

        $encryptCert = $res['data'][0]['encrypt_certificate'];
        $ciphertext = base64_decode($encryptCert['ciphertext']);
        $aad = $encryptCert['associated_data'];
        $nonce = $encryptCert['nonce'];

        $certPem = sodium_crypto_aead_aes256gcm_decrypt($ciphertext, $aad, $nonce, $apiV3Key);

        if (!$certPem) {
            throw new \Exception('平台证书解密失败，请检查 APIv3 密钥是否正确');
        }

        file_put_contents($certPath, $certPem);

        $serial = PemUtil::parseCertificateSerialNo('file://' . $certPath);

        return [
            'serial_no' => $serial,
            'cert_path' => $certPath
        ];
    }
}